from dash import Dash, dcc, html, Input, Output
import dash_ag_grid as dag
import dash_bootstrap_components as dbc
import plotly.graph_objects as go
import pandas as pd
import numpy as np
df = pd.read_csv("Hamilton.csv")
# Remove any duplicate column
df = df.loc[:, ~df.columns.duplicated()]
# Convert the 'Date' column to datetime format
df['Date'] = pd.to_datetime(df['Date']).dt.date # Keep only date (YYYY-MM-DD)
# Extract year, month, week, and day from the 'Date' column for filtering
df['Year'] = pd.to_datetime(df['Date']).dt.year
df['Month'] = pd.to_datetime(df['Date']).dt.strftime('%b')
df['Week'] = "Week " + pd.to_datetime(df['Date']).dt.isocalendar().week.astype(str)
df['Day'] = pd.to_datetime(df['Date']).dt.day
# Convert numeric spending values
for col in ['Daily', 'Weekly', '7-day moving average']:
df[col] = pd.to_numeric(df[col], errors='coerce')
# Filter the dataset to include only the top 10 spending categories
top_categories = (
df[df['transaction_catg_renamed'] != "Total"]
.groupby('transaction_catg_renamed')['Weekly'].sum()
.nlargest(10)
.index
)
df = df[df['transaction_catg_renamed'].isin(top_categories)]
# months appear in correct order
month_order = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
# Create dropdown options with correct order
month_options = [{"label": m, "value": m} for m in month_order if m in df['Month'].unique()]
month_options.append({"label": "All", "value": "All"})
# Initialize the Dash app
app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
app.layout = dbc.Container([
dbc.Row([
dbc.Col(html.H1("Hamilton Project Spending Dashboard", className="text-center"), width=12)
], className="mb-4"),
dbc.Row([
dbc.Col(dcc.Dropdown(
id="year-dropdown",
options=[{"label": str(y), "value": y} for y in sorted(df['Year'].unique())] +
[{"label": "All", "value": "All"}],
value="All",
clearable=False,
style={"width": "100%"}
), width=3),
dbc.Col(dcc.Dropdown(
id="month-dropdown",
options=month_options,
value="All",
clearable=False,
style={"width": "100%"}
), width=3),
dbc.Col(dcc.Dropdown(
id="metric-dropdown",
options=[
{"label": "Daily", "value": "Daily"},
{"label": "Weekly", "value": "Weekly"},
{"label": "7-Day Moving Average", "value": "7-day moving average"},
],
value="Weekly",
clearable=False,
style={"width": "100%"}
), width=3),
], className="mb-4"),
# Radio button
dbc.Row([
dbc.Col(dcc.RadioItems(
id="chart-type",
options=[
{"label": "Bar Chart", "value": "bar"},
{"label": "Line Chart", "value": "line"}
],
value="bar",
inline=True,
labelStyle={"margin-right": "10px"}
), width=12)
], className="mb-4"),
# Chart
dbc.Row([
dbc.Col(dcc.Graph(id="chart"), width=12),
], className="mb-4"),
# Data table
dbc.Row([
dbc.Col(
dag.AgGrid(
id="data-table",
rowData=df.to_dict("records"),
columnDefs=[
{"field": "Date", 'filter': True, 'sortable': True},
{"field": "Year", 'filter': True, 'sortable': True},
{"field": "Month", 'filter': True, 'sortable': True},
{"field": "Week", 'filter': True, 'sortable': True},
{"headerName": "Transaction category", "field": "transaction_catg_renamed", 'filter': True, 'sortable': True},
{"field": "Daily", 'filter': True, 'sortable': True},
{"field": "Weekly", 'filter': True, 'sortable': True},
{"field": "7-day moving average", 'filter': True, 'sortable': True}
],
dashGridOptions={"pagination": True, "domLayout": "autoHeight"},
columnSize="sizeToFit",
style={"width": "100%"}
),
width=12
)
])
], fluid=True, style={"backgroundColor": "#f4f4f4", "padding": "30px"})
# -------------------------
# Callbacks for Interactivity
# -------------------------
@app.callback(
[Output("chart", "figure"), Output("data-table", "rowData")],
[Input("year-dropdown", "value"),
Input("month-dropdown", "value"),
Input("metric-dropdown", "value"),
Input("chart-type", "value")]
)
def update_content(selected_year, selected_month, selected_metric, chart_type):
filtered_df = df.copy()
if selected_year != "All":
filtered_df = filtered_df[filtered_df['Year'] == selected_year]
if selected_month != "All":
filtered_df = filtered_df[filtered_df['Month'] == selected_month]
# Set x and y axis data based on metric selection
if selected_metric == "Weekly":
x_axis_data, y_axis_data = filtered_df['Week'], filtered_df['Weekly']
elif selected_metric == "Daily":
x_axis_data, y_axis_data = filtered_df['Day'], filtered_df['Daily']
else:
x_axis_data, y_axis_data = filtered_df['Date'], filtered_df['7-day moving average']
fig = go.Figure()
# Loop through categories and add traces based on selected chart type
for category in filtered_df['transaction_catg_renamed'].unique():
category_data = filtered_df[filtered_df['transaction_catg_renamed'] == category]
if chart_type == "bar":
fig.add_trace(go.Bar(
x=category_data[x_axis_data.name],
y=category_data[y_axis_data.name],
name=category
))
else: # Line Chart
fig.add_trace(go.Scatter(
x=category_data[x_axis_data.name],
y=category_data[y_axis_data.name],
mode="lines",
name=category
))
# Update layout
fig.update_layout(
title=f"{selected_metric} Spending Over Time",
xaxis_title="Time",
yaxis_title="Spending in Billions USD",
plot_bgcolor='white',
barmode='stack' if chart_type == "bar" else None,
dragmode="drawopenpath", #default setting
#dragmode="zoom",
modebar_add=["drawopenpath", "drawclosedpath", "eraseshape"]
)
return fig, filtered_df.to_dict("records")
if __name__ == "__main__":
app.run(debug=True)